package com.nsharmon.dao.entity;

import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Transient;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;

import com.nsharmon.dao.entity.ComparisonAxiom.BlankThanVariant;
import com.nsharmon.dao.entity.ComparisonAxiom.GreaterThanVariant;
import com.nsharmon.dao.entity.ComparisonAxiom.LessThanVariant;
import com.nsharmon.l18n.L18n;
import com.nsharmon.webapp.listeners.HibernateSessionListener;

@Entity
@DiscriminatorValue("comparison")
public class ComparisonCondition extends Condition {
	public ComparisonCondition() {
		setVariant(ComparisonAxiom.BlankThanVariant.IsBlankThan);
	}

	@Transient
	public EntityObject getLesser() {
		return getEntities().get(1);
	}
	
	@Transient
	public void setLesser(EntityObject lesser) {
		if(getEntities().size() <= 1) {
			if(getEntities().size() == 0) {
				getEntities().add(null);
			}
			getEntities().add(lesser);
		} else {
			getEntities().set(1, lesser);
		}
	}
	
	
	@Transient
	public EntityObject getGreater() {
		return getEntities().get(0);
	}
	
	@Transient
	public void setGreater(EntityObject greater) {
		if(getEntities().size() == 0) {
			getEntities().add(greater);
		} else {
			getEntities().set(0, greater);
		}
	}
	
	@Transient
	public String toString(Enum<?> variant) {
		StringBuilder sb = new StringBuilder();
		List<EntityObject> entities = getEntities();
		for(int i=1; i<entities.size(); i++) {
			sb.append(entities.get(i));
			if(i + 1 < entities.size()) {
				sb.append(", ");
			}
		}
		List<Parameter> params = getParameters();
		if(params.size() == 0) {
			return L18n.getMessage(getClass(), variant.toString(), entities.get(0), sb.toString());
		} else {
			Object[] parameters = new Object[2 + params.size()];
			parameters[0] = entities.get(0);
			parameters[1] = sb.toString();
			for(int i=0; i<params.size(); i++) {
				parameters[i+2] = params.get(i);
			}
			return L18n.getMessage(getClass(), variant.toString(), parameters);
		}
	}
	
	@Transient
	@Override
	public String toString() {
		return toString(getVariant());
	}
	
	@Transient
	public static ComparisonCondition isLessThan(LessThanVariant variant, String entity1Name, String entity2Name) {
		BlankThanVariant blankThanVariant = convertToBlankThanVariant(variant);		
		return isBlankThan(null, blankThanVariant, entity1Name, entity2Name, L18n.getMessage(ComparisonAxiom.class, "less"));
	}

	@Transient
	public static ComparisonCondition isGreaterThan(GreaterThanVariant variant, String entity1Name, String entity2Name ) {
		BlankThanVariant blankThanVariant = convertToBlankThanVariant(variant);			
		return isBlankThan(null, blankThanVariant, entity2Name, entity1Name, L18n.getMessage(ComparisonAxiom.class, "less"));
	}

	@Transient
	public static ComparisonCondition isGreaterThan(Session session, GreaterThanVariant variant, String entity1Name, String entity2Name ) {
		BlankThanVariant blankThanVariant = convertToBlankThanVariant(variant);			
		return isBlankThan(session, blankThanVariant, entity2Name, entity1Name, L18n.getMessage(ComparisonAxiom.class, "less"));
	}
	
	@Transient
	public static ComparisonCondition asBlankAs(Session session, String entity1Name, String entity2Name, String param) {
		return isBlankThan(session, BlankThanVariant.AsBlankAs, entity2Name, entity1Name, param);
	}
	
	@Transient
	public static BlankThanVariant convertToBlankThanVariant(LessThanVariant variant) {
		return BlankThanVariant.values()[variant.ordinal()];
	}
	
	@Transient
	public static BlankThanVariant convertToBlankThanVariant(GreaterThanVariant variant) {
		return BlankThanVariant.values()[variant.ordinal()];
	}
	
	@Transient
	public static ComparisonCondition isBlankThan(Session session, BlankThanVariant variant, String entity1Name, String entity2Name, String ... params) {
		ComparisonCondition condition = null;
		boolean localSession = false;
		Transaction trx = null;
		if(session == null) {
			localSession = true;
			session = HibernateSessionListener.openSession();
			trx = session.beginTransaction();
		}
		
		try {
			condition = new ComparisonCondition();
			condition.setVariant(variant);
			
			EntityObject entity1 = EntityObject.getOrCreate(session, entity1Name);
			condition.getEntities().add(entity1);
			
			EntityObject entity2 = EntityObject.getOrCreate(session, entity2Name);
			condition.getEntities().add(entity2);
			
			for(String param : params) {
				Parameter parameter = new Parameter(param);
				condition.getParameters().add(parameter);
			}
			
			session.save(condition);
			
			if(localSession) {
				trx.commit();
			}
		} catch(HibernateException e) {
			condition = null;
			
			if(localSession) {
				trx.rollback();
			}
			throw e;
		} finally {
			if(localSession) {
				session.close();
			}
		}
		return condition;		
	}
	
	@Override
	public boolean evaluate(Set<Axiom> axioms) {
		boolean evaluation = false;
		Iterator<Axiom> it = axioms.iterator();
		while(it.hasNext()) {
			Axiom axiom = it.next();
			if(axiom instanceof ComparisonAxiom && getEntities().equals(axiom.getEntities()) && getParameters().equals(axiom.getParameters())) {
				ComparisonAxiom ca = (ComparisonAxiom)axiom;

				// This condition tests one of the following:  A < B, A <= B, A = B
				// At this point we're guaranteed that operands are in same order, meaning
				// If condition is A <= B, then evaluation is true.  Otherwise if our condition is A < B, then statement must be A < B.
				// Similarly, if condition is A = B then statement must be A = B.  
				if(getVariant().equals(BlankThanVariant.IsBlankThanOrEqualTo) || ca.getVariant().equals(getVariant())) {
					evaluation = true;
					break;
				}
			}
		}
		return evaluation;
	}
}
